package library.image;

import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.MediaMetadataRetriever;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;

import library.http.Http;
import library.http.HttpTask;
import library.logger.Logger;
import library.utils.ApkUtils;
import okhttp3.ResponseBody;

/**
 * Created by Mr.Fan on 2016/2/18.
 */
public class BitmapUtils {

    private static final Object lock = new Object();
    private static DiskLruCache mDiskLruCache = null;
    private static File cacheDir;

    private BitmapUtils() {
    }

    public static void init(Context context) {
        if (mDiskLruCache != null && !mDiskLruCache.isClosed()) {
            return;
        }
        synchronized (lock) {
            if (mDiskLruCache != null && !mDiskLruCache.isClosed()) {
                return;
            }
            try {
                cacheDir = getDiskCacheDir(context, "bitmap");
                if (!cacheDir.exists()) {
                    cacheDir.mkdirs();
                }
                mDiskLruCache = DiskLruCache.open(cacheDir, ApkUtils.getVersionCode(context),
                        /*一个Key对应*/1/*个值*/, 32 * 1024 * 1024);
            } catch (IOException e) {
                Logger.debug(e);
            }
        }
    }

    public static void cacheClean() {
        try {
            if (mDiskLruCache != null)
                mDiskLruCache.delete();
        } catch (Exception e) {
            Logger.debug(e);
        }
    }

    public static long cacheSize() {
        try {
            if (mDiskLruCache != null)
                return mDiskLruCache.size();
        } catch (Exception e) {
            Logger.debug(e);
        }
        return 0;
    }

    private static Paint newPaint() {
        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        return paint;
    }

    private static File getDiskCacheDir(Context context, String uniqueName) {
        File cacheFile;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            cacheFile = context.getExternalCacheDir();
        } else {
            cacheFile = context.getCacheDir();
        }
        if (cacheFile != null && !cacheFile.exists())
            cacheFile.mkdirs();
        return new File(cacheFile.getAbsolutePath() + File.separator + uniqueName);
    }

    private static void calculateInSampleSize(BitmapFactory.Options options, float reqWidth, float reqHeight) {
        if (reqWidth > 0 && reqHeight > 0) {
            final int height = options.outHeight;
            final int width = options.outWidth;
            if (height > reqHeight || width > reqWidth) {
                final int heightRatio = Math.round((float) height / (float) reqHeight);
                final int widthRatio = Math.round((float) width / (float) reqWidth);
                options.inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
            }
        }
    }

    public static BitmapFactory.Options getBitmapOptions(String file, float width, float height) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        if (width > 0 && height > 0) {
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(file, options);
        }
        return getBitmapOptions(options, width, height);
    }

    public static BitmapFactory.Options getBitmapOptions(InputStream is, float width, float height) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        if (width > 0 && height > 0) {
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, options);
        }
        return getBitmapOptions(options, width, height);
    }

    private static BitmapFactory.Options getBitmapOptions(BitmapFactory.Options options, float width, float height) {

        calculateInSampleSize(options, width, height);
        options.inJustDecodeBounds = false;

        options.inPurgeable = true; // bitmap can be purged to disk
        options.inInputShareable = true;
        return options;
    }

    protected static Bitmap getBitmapFromStream(final BitmapOption bitmapOption) {
        InputStream is = null;
        try {
            final Context context = bitmapOption.getContext();
            if (context == null)
                return null;
            is = (InputStream) bitmapOption.getSrc();
            final float width = bitmapOption.getWidth();
            final float height = bitmapOption.getHeight();
            Bitmap bitmap = decodeStreamToBitmap(is, width, height);
            return bitmap;
        } catch (OutOfMemoryError e) {
            Logger.debug(e);
        } catch (Exception e) {
            Logger.debug(e);
        } finally {
            if (bitmapOption.isAutoClose() && is != null) {
                try {
                    is.close();
                } catch (Exception e) {
                }
            }
        }
        return null;
    }

    protected static Bitmap getBitmapFromResource(final BitmapOption bitmapOption) {
        InputStream is = null;
        try {
            final Context context = bitmapOption.getContext();
            if (context == null)
                return null;
            final int resId = Integer.parseInt(String.valueOf(bitmapOption.getSrc()));
            final float width = bitmapOption.getWidth();
            final float height = bitmapOption.getHeight();
            is = context.getResources().openRawResource(resId);
//            BitmapFactory.Options options = getBitmapOptions(is, width, height);
            //Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
            Bitmap bitmap = decodeStreamToBitmap(is, width, height);
            return bitmap;
        } catch (OutOfMemoryError e) {
            Logger.debug(e);
        } catch (Exception e) {
            Logger.debug(e);
        } finally {
            if (is != null)
                try {
                    is.close();
                } catch (IOException e) {
                }
        }
        return null;
    }

    protected static Bitmap getBitmapFromFile(final BitmapOption bitmapOption) {
        final String file = String.valueOf(bitmapOption.getSrc());
        final float width = bitmapOption.getWidth();
        final float height = bitmapOption.getHeight();
        BitmapFactory.Options options = getBitmapOptions(file, width, height);
        try {
            Bitmap bitmap = BitmapFactory.decodeFile(file, options);
            return bitmap;
        } catch (OutOfMemoryError e) {
            Logger.debug(e);
        } catch (Exception e) {
            Logger.debug(e);
        }
        return null;
    }


    protected static Bitmap getBitmapFromAssets(final BitmapOption bitmapOption) {
        InputStream is = null;
        try {
            final Context context = bitmapOption.getContext();
            if (context == null)
                return null;
            final String path = String.valueOf(bitmapOption.getSrc());
            final float width = bitmapOption.getWidth();
            final float height = bitmapOption.getHeight();
            AssetManager am = context.getAssets();
            String raw = path;
            raw = raw.startsWith(File.separator) ? raw.substring(1) : raw;
            is = am.open(raw);
            Bitmap bitmap = decodeStreamToBitmap(is, width, height);
            return bitmap;
        } catch (OutOfMemoryError e) {
            Logger.debug(e);
        } catch (Exception e) {
            Logger.debug(e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (Exception e) {
                }
            }
        }
        return null;
    }

    protected static Bitmap getBitmapFromUrl(final BitmapOption bitmapOption) {
        if (mDiskLruCache == null || mDiskLruCache.isClosed())
            init(bitmapOption.getContext());
        if (mDiskLruCache == null || mDiskLruCache.isClosed())
            return null;
        DiskLruCache.Snapshot snapShot = null;
        InputStream is = null;
        try {
            final float width = bitmapOption.getWidth();
            final float height = bitmapOption.getHeight();
            HttpTask httpTask = bitmapOption.getHttpTask();
            if (httpTask == null) {
                httpTask = Http.obtain(bitmapOption.getContext())
                        .url(String.valueOf(bitmapOption.getSrc()));
            }
            String key = BitmapCache.key(
                    String.valueOf(bitmapOption.getFrom()),
                    httpTask.getUrl());

            snapShot = mDiskLruCache.get(key);

            if (snapShot != null) {
                is = snapShot.getInputStream(0);
            }
            if (is == null) {
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                if (editor == null) {
                    while (snapShot == null) {
                        snapShot = mDiskLruCache.get(key);
                        if (snapShot == null) {
                            Thread.sleep(40);
                        }
                    }
                }
                OutputStream outputStream = editor.newOutputStream(0);
                if (downloadToStream(httpTask, outputStream)) {
                    editor.commit();
                } else {
                    editor.abort();
                    throw new Exception("image download failed");
                }
                mDiskLruCache.flush();
                snapShot = mDiskLruCache.get(key);
                if (snapShot != null) {
                    is = snapShot.getInputStream(0);
                }
            }

            if (is != null) {
                Bitmap bitmap = decodeStreamToBitmap(is, width, height);
                if (bitmap != null)
                    return bitmap;
                else {
                    mDiskLruCache.remove(key);
                }
            }
        } catch (OutOfMemoryError e) {
            Logger.debug(e);
        } catch (Exception e) {
            Logger.debug(e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {

                }
            }
            if (snapShot != null) {
                snapShot.close();
            }
        }
        return null;
    }

    protected static Bitmap getBitmapFromVideoThumbnail(final BitmapOption bitmapOption) {
        if (Build.VERSION.SDK_INT < 10)
            return null;
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            final String path = String.valueOf(bitmapOption.getSrc());

            if (Build.VERSION.SDK_INT < 14) {
                retriever.setDataSource(path);
            } else {
                retriever.setDataSource(path, new HashMap<String, String>());
            }
            Bitmap bitmap = retriever.getFrameAtTime();
            return bitmap;
        } catch (Exception e) {
            Logger.debug(e);
        } finally {
            try {
                retriever.release();
            } catch (RuntimeException e) {
                Logger.debug(e);
            }
        }
        return null;
    }

    private static boolean downloadToStream(HttpTask task, OutputStream outputStream) {
        BufferedOutputStream out = null;
        BufferedInputStream in = null;
        ResponseBody responseBody = null;
        try {
            responseBody = task.execute(ResponseBody.class);
            in = new BufferedInputStream(responseBody.byteStream(), 8 * 1024);
            out = new BufferedOutputStream(outputStream, 8 * 1024);
            int b;
            while ((b = in.read()) != -1) {
                out.write(b);
            }
            out.flush();
            return true;
        } catch (final IOException e) {
            Logger.debug(e);
        } catch (Exception e) {
            Logger.debug(e);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {

                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {

                }
            }
            if (responseBody != null) {
                responseBody.close();
            }
        }
        return false;
    }

    public static Bitmap bitmapToGrayScale(Bitmap from) {
        int width, height;
        height = from.getHeight();
        width = from.getWidth();

        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
        Canvas c = new Canvas(bitmap);
        Paint paint = newPaint();
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
        paint.setColorFilter(f);
        c.drawBitmap(from, 0, 0, paint);
        try {
            from.recycle();
        } catch (Exception e) {

        }
        return bitmap;
    }

    public static Bitmap bitmapToCorner(Bitmap from, float corner) {
        if (from == null)
            return null;
        int width = from.getWidth();
        int height = from.getHeight();

        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = newPaint();
        Rect rect = new Rect(0, 0, from.getWidth(), from.getHeight());
        RectF rectF = new RectF(rect);

        paint.setColor(0xFF424242);
        canvas.drawRoundRect(rectF, corner, corner, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(from, null, rect, paint);
        try {
            from.recycle();
        } catch (Exception e) {

        }
        return bitmap;
    }

    public static Bitmap bitmapToRound(Bitmap bitmap) {
        return bitmapToRound(bitmap, null);
    }

    public static Bitmap bitmapToRound(Bitmap bitmap, BitmapOption option) {
        if (bitmap == null)
            return null;
        final int width = bitmap.getWidth();
        final int height = bitmap.getHeight();
        final int radius = Math.min(width, height) / 2;

        final Rect dst = new Rect(0, 0, radius * 2, radius * 2);
        final Rect src = new Rect(width / 2 - radius, height / 2 - radius, width / 2 + radius, height / 2 + radius);

        Bitmap output = Bitmap.createBitmap(dst.width(), dst.height(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int shadowRadius = (int) option.getShadowWidth();
        final int borderWidth = (int) option.getBorderWidth();

        final int cx = dst.width() / 2;
        final int cy = dst.height() / 2;
        final int inset = borderWidth + shadowRadius + 1;
        final int r = radius - inset;

        final RectF target = new RectF(cx - r, cy - r, cx + r, cy + r);

        Paint paint;

        if (option != null && (shadowRadius > 0 || borderWidth > 0)) {
            paint = newPaint();
            paint.setStrokeWidth(borderWidth);
            paint.setColor(option.getBorderColor());
            paint.setShadowLayer(shadowRadius, 0.0f, 0.0f, option.getShadowColor());
            canvas.drawCircle(cx, cy, r + borderWidth, paint);
        }
        canvas.saveLayerAlpha(0, 0, radius * 2, radius * 2, 0xFF, Canvas.ALL_SAVE_FLAG);

        paint = newPaint();
        paint.setColor(0xFF424242);
        canvas.drawCircle(cx, cy, r, paint);
        paint = newPaint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, src, target, paint);

        canvas.restore();
        try {
            bitmap.recycle();
        } catch (Exception e) {

        }
        return output;
    }

    private static Bitmap decodeStreamToBitmap(InputStream is, float width, float height) {
        byte[] data = decodeStreamToArray(is);
        InputStream is1 = new ByteArrayInputStream(data);
        InputStream is2 = new ByteArrayInputStream(data);
        BitmapFactory.Options options = getBitmapOptions(is1, width, height);
        Bitmap bitmap = BitmapFactory.decodeStream(is2, null, options);
        return bitmap;
    }

    private static byte[] decodeStreamToArray(InputStream is) {
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024]; // 用数据装
            int len = -1;
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            byte[] data = baos.toByteArray();
            return data;
        } catch (OutOfMemoryError e) {
            Logger.debug(e);
        } catch (Exception e) {
            Logger.debug(e);
        } finally {
            if (baos != null)
                try {
                    baos.close();
                } catch (Exception e) {
                }
        }
        return null;
    }

    public static Bitmap getScaleBitmap(Bitmap bitmapFrom, float scale) {
        if (bitmapFrom == null || scale == 1 || scale == 0)
            return bitmapFrom;
        int widthFrom = bitmapFrom.getWidth();
        int heightFrom = bitmapFrom.getHeight();

        Matrix matrix = new Matrix();
        matrix.setScale(scale, scale);

        Bitmap bitmapTo = Bitmap.createBitmap(bitmapFrom, 0, 0, widthFrom, heightFrom, matrix, true);
        try {
            bitmapFrom.recycle();
        } catch (Exception e) {

        }
        return bitmapTo;
    }

    public static Bitmap getScaleBitmap(Bitmap bitmapFrom, float width, float height) {
        if (bitmapFrom == null || width == 0 || height == 0)
            return bitmapFrom;
        int widthFrom = bitmapFrom.getWidth();
        int heightFrom = bitmapFrom.getHeight();
        float widthTo = width;
        float heightTo = height;
        if (widthTo == widthFrom && heightTo == heightFrom)
            return bitmapFrom;
        float scaleWidth = ((float) widthTo) / widthFrom;
        float scaleHeight = ((float) heightTo) / heightFrom;

        Matrix matrix = new Matrix();
        matrix.setScale(scaleWidth, scaleHeight);

        Bitmap bitmapTo = Bitmap.createBitmap(bitmapFrom, 0, 0, widthFrom, heightFrom, matrix, true);
        try {
            bitmapFrom.recycle();
        } catch (Exception e) {

        }
        return bitmapTo;
    }

    public static File saveToFile(final Bitmap bitmap, final String path, Bitmap.CompressFormat format) {
        if (TextUtils.isEmpty(path))
            return null;
        return saveToFile(bitmap, new File(path), format);
    }

    public static File saveToFile(final Bitmap bitmap, final File file, Bitmap.CompressFormat format) {
        if (bitmap == null || file == null)
            return null;
        if (format == null)
            format = Bitmap.CompressFormat.PNG;

        BufferedOutputStream bos = null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(file));
            bitmap.compress(format, 100, bos);
            bos.flush();
            if (file.exists() && file.length() > 0)
                return file;
        } catch (Exception e) {
            Logger.debug(e);
        } finally {
            if (bos != null)
                try {
                    bos.close();
                } catch (IOException e) {
                }
        }
        return null;
    }

}
